using System;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Text;
using System.Threading;
using Microsoft.Phone.BackgroundAudio;

namespace WindowsPhoneAudioStreaming.Audio.Streaming {
    public class DownloadTracker {
        public static readonly Mutex TrackerMutex = new Mutex(false, "LogFileTracker");

        public enum NetworkType {
            Cellular,
            Wireless,
            Ethernet
        }

        /// <summary>
        /// Number of millisecs between updating log of bytes downloaded by connection
        /// </summary>
        private readonly int logUpdateInterval;
        private readonly int CellularThreshold;

        /// <summary>
        /// When the log file was first written to
        /// </summary>
        private DateTime LogStartDate { get; set; }
        private DateTime LogToday { get; set; }

        /// <summary>
        /// When the log file was last updated
        /// </summary>
        private int Updated { get; set; }

        /// <summary>
        /// The downloaded bytes
        /// </summary>
        private long[] TotalBytesByConnection { get; set; }
        private long[] TodayBytesByConnection { get; set; }
        public string ConnectionType { get; set; }

        /// <summary>
        /// The string builder used to create the log output to be written to file system
        /// </summary>
        private readonly StringBuilder logBuilder = new StringBuilder();

        /// <summary>
        /// Protects the actual log writing from multiple entrants
        /// </summary>
        private int logProtector;

        /// <summary>
        /// File in iso storage that houses the log file
        /// </summary>
        public const string LogFileName = "Streaming.DownloadLog.txt";


        public DownloadTracker(int updateIntervalInSeconds, int cellularStreamingThreshold) {
            logUpdateInterval = updateIntervalInSeconds * 1000;
            CellularThreshold = cellularStreamingThreshold;
            TotalBytesByConnection = new long[3];
            TodayBytesByConnection = new long[3];

            LogLoad();
            Updated = Environment.TickCount;
        }


        private void LogLoad() {
            string status = string.Empty;
            TrackerMutex.WaitOne();
            try {

                using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
                    try {
                        if (store.FileExists(LogFileName)) {
                            using (
                                var istream = new IsolatedStorageFileStream(LogFileName, FileMode.Open,
                                                                            FileAccess.Read, store))
                            using (var sw = new StreamReader(istream)) {
                                status = sw.ReadToEnd();
                            }
                        }
                    }
                    catch (Exception ex) {
                        Debug.WriteLine(ex.Message);
                    }
                }
            }
            finally {
                TrackerMutex.ReleaseMutex();
            }

            if (status != string.Empty) {
                try {
                    string[] values = status.Split('%');
                    if (values.Length == 9) {
                        LogStartDate = DateTime.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture);
                        LogToday = DateTime.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture);

                        //get total download byte counts by connection type
                        TotalBytesByConnection[(int)NetworkType.Cellular] = long.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture);
                        TotalBytesByConnection[(int)NetworkType.Wireless] = long.Parse(values[3], System.Globalization.CultureInfo.InvariantCulture);
                        TotalBytesByConnection[(int)NetworkType.Ethernet] = long.Parse(values[4], System.Globalization.CultureInfo.InvariantCulture);

                        // Get Todays download byte counts by connection type
                        TodayBytesByConnection[(int)NetworkType.Cellular] = long.Parse(values[5], System.Globalization.CultureInfo.InvariantCulture);
                        TodayBytesByConnection[(int)NetworkType.Wireless] = long.Parse(values[6], System.Globalization.CultureInfo.InvariantCulture);
                        TodayBytesByConnection[(int)NetworkType.Ethernet] = long.Parse(values[7], System.Globalization.CultureInfo.InvariantCulture);

                        // no need to load old connection type values[8]
                    }
                    else {
                        Debug.WriteLine("Incorrect number values from data tracking log, expected 8");
                    }
                }
                // just swallow the exception
                catch (Exception ex) {
                    Debug.WriteLine(ex.Message);
                }
            }
        }


        public void UpdateBytesDownloaded(int byteCount, Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType networkInterface) {
            // if new day then clear the day download count
            if (LogToday.Date != DateTime.Now.Date) {
                LogToday = DateTime.Now.Date;
                TodayBytesByConnection[0] = TodayBytesByConnection[1] = TodayBytesByConnection[2] = 0;
            }

            ConnectionType = networkInterface.ToString();

            try {
                switch (networkInterface) {
                    case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.MobileBroadbandCdma:
                    case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.MobileBroadbandGsm:
                        TotalBytesByConnection[(int)NetworkType.Cellular] += byteCount;
                        TodayBytesByConnection[(int)NetworkType.Cellular] += byteCount;
                        break;
                    case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.Wireless80211:
                        TotalBytesByConnection[(int)NetworkType.Wireless] += byteCount;
                        TodayBytesByConnection[(int)NetworkType.Wireless] += byteCount;
                        break;
                    case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.Ethernet:
                        TotalBytesByConnection[(int)NetworkType.Ethernet] += byteCount;
                        TodayBytesByConnection[(int)NetworkType.Ethernet] += byteCount;
                        break;
                }
                LogDownloadCount();
            }
            catch (Exception ex) {
                // Make sure we don't bork!
                Debug.WriteLine(ex.Message);
            }
        }


        private void LogDownloadCount() {
            // this will be a v fast download count writer
            if (Environment.TickCount - Updated > logUpdateInterval) {
                Updated = Environment.TickCount;
                if (Interlocked.CompareExchange(ref logProtector, 1, 0) == 1) return;
                ThreadPool.QueueUserWorkItem(WriteLog, TotalBytesByConnection);
                //if (TodayBytesByConnection[(int)NetworkType.Wireless] > CellularThreshold && CellularThreshold != 0) {
                //    BackgroundAudioPlayer.Instance.Stop();
                //}
            }
        }

        private void WriteLog(object cb) {
            try {
                if (LogStartDate == DateTime.MinValue) {
                    LogStartDate = DateTime.Now;
                }

                logBuilder.Remove(0, logBuilder.Length);

                logBuilder.Append(LogStartDate.ToString(System.Globalization.CultureInfo.InvariantCulture));
                logBuilder.Append("%" + LogToday.ToString(System.Globalization.CultureInfo.InvariantCulture));

                for (int i = 0; i < TotalBytesByConnection.Length; i++) {
                    logBuilder.Append("%" + TotalBytesByConnection[i].ToString(System.Globalization.CultureInfo.InvariantCulture));
                }

                for (int i = 0; i < TodayBytesByConnection.Length; i++) {
                    logBuilder.Append("%" + TodayBytesByConnection[i].ToString(System.Globalization.CultureInfo.InvariantCulture));
                }

                logBuilder.Append("%" + ConnectionType);

                TrackerMutex.WaitOne();
                try {
                    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                    using (var istream = new IsolatedStorageFileStream(LogFileName, FileMode.Create, FileAccess.Write, store))
                    using (var sw = new StreamWriter(istream)) {
                        sw.Write(logBuilder.ToString());
                    }
                }
                finally {
                    TrackerMutex.ReleaseMutex();
                }

            }
            // swallow the exception possible conflict with client app reading the log
            catch (Exception ex) {
                Debug.WriteLine(ex.Message);
            }
            finally {
                Interlocked.Exchange(ref logProtector, 0);
            }
        }

        public void Flush()
        {
            for (var i = 0; i < TotalBytesByConnection.Length; i++)
            {
                TotalBytesByConnection[i] = 0;
            }
            for (var i = 0; i < TodayBytesByConnection.Length; i++)
            {
                TodayBytesByConnection[i] = 0;
            }

            // Force a write of the new values to disk
            if (Interlocked.CompareExchange(ref logProtector, 1, 0) == 1) return;
            ThreadPool.QueueUserWorkItem(WriteLog, TotalBytesByConnection);
        }
    }
}